/*
 * Copyright (c) 2009, Hamish Morgan. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the University of Sussex nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package piccat.view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import javax.swing.event.ChangeEvent;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.OverlayLayout;
import javax.swing.event.ChangeListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import piccat.diamondtouch.DTHandler;
import piccat.model.Game;
import piccat.model.Side;
import piccat.model.Side.PictureAddedEvent;
import piccat.model.Side.PictureDroppedEvent;
import piccat.model.Side.PicturePickupEvent;
import piccat.model.Side.ReadinessChangedEvent;
import piccat.model.Side.SideChangeListener;

/**
 * Top level view of the game model. Doesn't do much because the main logic is
 * divided between the <tt>SideView</tt> objects.
 * 
 * @author Hamish Morgan
 */
public class GameView extends JPanel {

    /**
     * Whether or not to give "helpful" messages at the top of the game
     * screen. It was requested that these be hidden after initial testing.
     */
    private static final boolean SHOW_STATUS_MESSAGES = false;
    /**
     * The delay in milliseconds between all "We agree" buttons being pressed,
     * and new pictures being given. This allows the players to revieve
     * additional feedback about what is about to happen.
     */
    private static final int GIVE_PICTURES_DELAY = 1000;
    /**
     * Log of general system events, warnings, and errors.
     */
    protected final Logger log = LoggerFactory.getLogger(
            this.getClass().getName());
    //
    private final ResourceBundle strings = ResourceBundle.getBundle(
            "strings", Locale.getDefault());
    //
    private final GameStateChangeHandler gameListener;
    private final List<SideView> sides;
    private final SideChangeHandler sideHandler;
    private Game game;
    private JLabel statusLabel;
    private JPanel centerPanel;
    private GlassPane glass;
    private DTHandler dtHandler;

    public GameView(Game model) {
        this();
        setModel(model);
    }

    public GameView() {
        sides = new ArrayList<SideView>(2);
        gameListener = new GameStateChangeHandler();
        sideHandler = new SideChangeHandler();
        dtHandler = null;

        initComponents();
    }

    public void setDTHandler(DTHandler newDtHandler) {
        // If it's the same object then don't bother doing anything
        if (this.dtHandler == newDtHandler)
            return;

        // Clear the old handler
        if (this.dtHandler != null) {
            for (SideView side : sides)
                this.dtHandler.removeListener(side.getTouchHandler());
        }

        // Set the new handler
        if (newDtHandler != null) {
            for (SideView side : sides)
                newDtHandler.addListener(side.getTouchHandler());
            newDtHandler.getTransform().watchComponent(this);
        }

        this.dtHandler = newDtHandler;
    }

    private void initComponents() {

        setLayout(new OverlayLayout(this));
        setBackground(Color.white);

        glass = new GlassPane();

        JPanel background = new JPanel();
        background.setOpaque(false);
        background.setLayout(new BorderLayout());


        centerPanel = new JPanel();
        centerPanel.setOpaque(false);


        background.add(centerPanel, BorderLayout.CENTER);

        if (SHOW_STATUS_MESSAGES) {
            JPanel statusPanel = new JPanel();
            statusPanel.setOpaque(false);
            statusPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
            statusLabel = new JLabel();
            statusPanel.add(statusLabel);
            background.add(statusPanel, BorderLayout.NORTH);
        }

        add(glass);
        add(background);
    }

    public Game getModel() {
        return game;
    }

    public void clearModel() {
        if (game == null)
            return;

        (game).removeChangeListener(gameListener);
        centerPanel.removeAll();
        for (SideView side : sides) {
            side.getModel().removeChangeListener(sideHandler);
            side.setModel(null);
        }

        sides.clear();
        if (SHOW_STATUS_MESSAGES) {
            statusLabel.setText("");
        }
        glass.removeAll();
    }

    public void setModel(Game model) {
        if (model == game)
            return;

        clearModel();

        game = model;

        if (game != null) {
            centerPanel.setLayout(new GridLayout(1, model.getNumSides()));

            for (int i = 0; i < model.getNumSides(); i++) {
                List<Integer> ids = new ArrayList<Integer>();
                for (int j = i; j <= 8; j += model.getNumSides())
                    ids.add(j);
                SideView side = new SideView(glass, ids);

                side.setModel(model.getSide(i));
                model.getSide(i).addChangeListener(sideHandler);
                sides.add(side);
                centerPanel.add(side);
            }

            game.addChangeListener(gameListener);
            updateStatusText();
        }
    }

    private void doButtonsFail() {

        if (game.isValid() && game.isComplete() && game.isMatching())
            return;


        for (SideView side : sides) {
            Side model = side.getModel();
            if (model.isReady())
                side.doButtonFail();
        }

        if (SHOW_STATUS_MESSAGES) {
            if (!game.isComplete())
                statusLabel.setText(strings.getString("FAIL_COMPLETENESS"));
            else if (!game.isMatching())
                statusLabel.setText(strings.getString("FAIL_MATCHING"));
            else if (!game.isValid())
                statusLabel.setText(strings.getString("FAIL_VALIDITY"));
        }

    }

    class SideChangeHandler implements SideChangeListener {

        public void pictureAdded(PictureAddedEvent e) {
        }

        public void readinessChanged(ReadinessChangedEvent e) {
            if (e.getSource().isReady())
                doButtonsFail();
        }

        public void picturePickedUp(PicturePickupEvent e) {
            doButtonsFail();
        }

        public void pictureDropped(PictureDroppedEvent e) {
            doButtonsFail();
        }
    }

    private void updateStatusText() {
        if (!SHOW_STATUS_MESSAGES)
            return;
        String text = strings.getString(game.getState().name());
        statusLabel.setText(text == null ? game.getState().name() : text);

    }

    private class GameStateChangeHandler implements ChangeListener {

        public void stateChanged(ChangeEvent e) {

            if (game.getState() == Game.State.GAME_OVER) {
                for (SideView side : sides) {
                    side.doEndGame();
                    glass.setFillColour(GlassPane.GREEN);
                    dtHandler.removeListener(side.getTouchHandler());
                    GameView.this.repaint();
                }

            } else if (game.getState() == Game.State.GIVE_PICTURE) {
                new Thread(new GivePicturesWorker()).start();
            }
            updateStatusText();
        }
    }

    private class GivePicturesWorker implements Runnable {

        public void run() {
            try {
                Thread.sleep(GIVE_PICTURES_DELAY);
            } catch (InterruptedException ex) {
                log.error(null, ex);
            }
            game.doGivePicture();
        }
    };
}
